IEnumerable<T> の foreach に相当する LINQ
-
03-07-2019 - |
質問
LINQ で次と同等のことを実行したいのですが、方法がわかりません。
IEnumerable<Item> items = GetItems();
items.ForEach(i => i.DoStuff());
実際の構文は何ですか?
解決
IEnumerable
のForEach拡張はありません。 List&lt; T&gt;
のみ。だからあなたはできる
items.ToList().ForEach(i => i.DoStuff());
別の方法として、独自のForEach拡張メソッドを作成します。
public static void ForEach<T>(this IEnumerable<T> enumeration, Action<T> action)
{
foreach(T item in enumeration)
{
action(item);
}
}
他のヒント
Fredrikは修正を提供しましたが、なぜこれが最初のフレームワークにないのかを検討する価値があります。私は、LINQクエリ演算子には副作用がないようにし、合理的に機能する世界観に適合する必要があると考えています。明らかにForEachはまったく反対です-純粋に副作用ベースのコンストラクトです。
これが悪いことだと言っているのではなく、決定の背後にある哲学的な理由を考えているだけです。
更新 7/17/2012:どうやらC#5.0以降、以下で説明する foreach
の動作が変更され、&quot; ネストされたラムダ式での foreach
反復変数の使用予期しない結果が生成されなくなりました。&quot;この回答は、C#&#8805;には適用されません。 5.0。
@John Skeetおよびforeachキーワードを好むすべての人。
&quot; foreach&quot;の問題C# 5.0より前 、それは同等の「理解のために」どのように矛盾しているということです。他の言語で動作し、私はそれがどのように動作するかを期待しています(個人的な意見は、他の人が読みやすさに関する意見を述べているという理由だけでここに述べられています)。 &quot; 修正されたクロージャーへのアクセス&quot;に関するすべての質問をご覧ください。 &quot; 有害と見なされるループ変数のクローズ&quot;。これは「有害」だけです「foreach」という方法のためC#で実装されています。
@Fredrik Kalsethの答えと機能的に同等の拡張メソッドを使用して、次の例を実行してください。
public static class Enumerables
{
public static void ForEach<T>(this IEnumerable<T> @this, Action<T> action)
{
foreach (T item in @this)
{
action(item);
}
}
}
過度に考案された例に対する謝罪。 Observableを使用しているのは、このようなことをするために完全に取得されていないためです。明らかに、このオブザーバブルを作成するためのより良い方法がありますが、私はポイントを実証しようとしています。通常、オブザーバブルにサブスクライブされたコードは非同期で実行され、別のスレッドで潜在的に実行されます。 「foreach」を使用すると、非常に奇妙で潜在的に非決定的な結果が生じる可能性があります。
&quot; ForEach&quot;を使用した次のテスト拡張メソッドのパス:
[Test]
public void ForEachExtensionWin()
{
//Yes, I know there is an Observable.Range.
var values = Enumerable.Range(0, 10);
var observable = Observable.Create<Func<int>>(source =>
{
values.ForEach(value =>
source.OnNext(() => value));
source.OnCompleted();
return () => { };
});
//Simulate subscribing and evaluating Funcs
var evaluatedObservable = observable.ToEnumerable().Select(func => func()).ToList();
//Win
Assert.That(evaluatedObservable,
Is.EquivalentTo(values.ToList()));
}
次はエラーで失敗します。
予想:&lt;と同等0、1、2、3、4、5、6、7、8、9&gt; しかし:&lt; 9、9、9、9、9、9、9、9、9、9、9&gt;
[Test]
public void ForEachKeywordFail()
{
//Yes, I know there is an Observable.Range.
var values = Enumerable.Range(0, 10);
var observable = Observable.Create<Func<int>>(source =>
{
foreach (var value in values)
{
//If you have resharper, notice the warning
source.OnNext(() => value);
}
source.OnCompleted();
return () => { };
});
//Simulate subscribing and evaluating Funcs
var evaluatedObservable = observable.ToEnumerable().Select(func => func()).ToList();
//Fail
Assert.That(evaluatedObservable,
Is.EquivalentTo(values.ToList()));
}
IEnumerable&lt; T&gt;
で利用可能な FirstOrDefault()
拡張機能を使用できます。述語から false
を返すことにより、各要素に対して実行されますが、実際に一致が見つからないことは気にしません。これにより、 ToList()
のオーバーヘッドが回避されます。
IEnumerable<Item> items = GetItems();
items.FirstOrDefault(i => { i.DoStuff(); return false; });
Fredrikのメソッドを使用して、戻り値の型を変更しました。
このように、このメソッドは他のLINQメソッドと同様に遅延実行をサポートします。
編集:これが明確でない場合、このメソッドの使用は、 ToList()で終了する必要があります。列挙可能。そうしないと、アクションは実行されません!
public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enumeration, Action<T> action)
{
foreach (T item in enumeration)
{
action(item);
yield return item;
}
}
そして、ここにそれを確認するためのテストがあります:
[Test]
public void TestDefferedExecutionOfIEnumerableForEach()
{
IEnumerable<char> enumerable = new[] {'a', 'b', 'c'};
var sb = new StringBuilder();
enumerable
.ForEach(c => sb.Append("1"))
.ForEach(c => sb.Append("2"))
.ToList();
Assert.That(sb.ToString(), Is.EqualTo("121212"));
}
最後に ToList()を削除すると、StringBuilderに空の文字列が含まれるため、テストが失敗します。これは、ForEachに列挙を強制するメソッドがないためです。
IEnumerableから副作用を排除する
LINQで次のようなことをしたいのですが、どうすればよいかわかりません:
他の人が指摘しているように、こちらおよび海外のLINQおよび IEnumerable
メソッドは、副作用なし。
本当に「何かをしたい」のですか? IEnumerableの各アイテムに?その場合、 foreach
が最良の選択です。ここで副作用が発生しても、人々は驚かない。
foreach (var i in items) i.DoStuff();
副作用を望まないのは間違いない
ただし、私の経験では、副作用は通常必要ありません。多くの場合、Jon Skeet、Eric Lippert、またはMarc GravellによるStackOverflow.comの回答を伴う簡単なLINQクエリが発見されるのを待っています。
いくつかの例
実際に値を集計(累積)するだけの場合は、 Aggregate
拡張メソッドを検討する必要があります。
items.Aggregate(initial, (acc, x) => ComputeAccumulatedValue(acc, x));
おそらく、既存の値から新しい IEnumerable
を作成します。
items.Select(x => Transform(x));
または、ルックアップテーブルを作成する場合:
items.ToLookup(x, x => GetTheKey(x))
可能性のリスト(完全に意図されたわけではない)は延々と続く。
エニュメレーションロールとして機能する場合は、各アイテムを生成する必要があります。
public static class EnumerableExtensions
{
public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enumeration, Action<T> action)
{
foreach (var item in enumeration)
{
action(item);
yield return item;
}
}
}
Microsoft による実験的リリースがあります。 LINQ への対話型拡張機能 (また NuGet で, 、 見る しろたん @sirotan その他のリンクについては)。の チャンネル9のビデオ それをよく説明しています。
そのドキュメントは XML 形式でのみ提供されます。これを実行しました サンドキャッスルのドキュメント より読みやすい形式にできるようにします。ドキュメント アーカイブを解凍して、 インデックス.html.
他の多くの優れた機能の中でも特に、期待される ForEach 実装が提供されます。これにより、次のようなコードを書くことができます。
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8 };
numbers.ForEach(x => Console.WriteLine(x*x));
PLINQ(.Net 4.0以降で利用可能)によると、次のことができます
IEnumerable<T>.AsParallel().ForAll()
IEnumerableで並列foreachループを実行します。
ForEachの目的は、副作用を引き起こすことです。 IEnumerableは、セットの遅延列挙用です。
この概念の違いは、検討すると非常に顕著です。
SomeEnumerable.ForEach(item =&gt; DataStore.Synchronize(item));
これは&quot; count&quot;を実行するまで実行されません。または&quot; ToList()&quot;またはその上に何か。 それは明らかに表現されたものではありません。
IEnumerable拡張機能を使用して反復のチェーンを設定し、それぞれのソースと条件でコンテンツを定義する必要があります。式ツリーは強力で効率的ですが、その性質を理解することを学ぶ必要があります。怠aroundな評価を無効にするいくつかの文字を保存するために、それらの周りのプログラミングだけでなく。
多くの人がそれについて言及しましたが、書き留めなければなりませんでした。これは最も明確で最も読みやすいものではありませんか?
IEnumerable<Item> items = GetItems();
foreach (var item in items) item.DoStuff();
短くてシンプルな(st)。
すでに多くの回答が指摘しているように、このような拡張メソッドを自分で簡単に追加できます。ただし、これを行いたくない場合は、BCLでこのようなことは認識していませんが、 System
名前空間にはまだオプションがあります。 a href = "https://www.nuget.org/packages/Rx-Main" rel = "nofollow noreferrer">リアクティブ拡張機能(そうでない場合は必要です):
using System.Reactive.Linq;
items.ToObservable().Subscribe(i => i.DoStuff());
メソッド名は少し異なりますが、最終結果はまさに探しているものです。
次のオプションがあります...
ParallelOptions parallelOptions = new ParallelOptions();
parallelOptions.MaxDegreeOfParallelism = 4;
#if DEBUG
parallelOptions.MaxDegreeOfParallelism = 1;
#endif
Parallel.ForEach(bookIdList, parallelOptions, bookID => UpdateStockCount(bookID));
もちろん、これはまったく新しいワームの缶を開きます。
ps(フォントについては申し訳ありませんが、システムが決定したものです)
この「機能的アプローチ」抽象化には大きな時間がかかります。言語レベルでは、副作用を防ぐものは何もありません。コンテナ内のすべての要素に対してラムダ/デリゲートを呼び出すことができる限り、「ForEach」を取得します;動作。
ここで、たとえばsrcDictionaryをdestDictionaryにマージする1つの方法(キーが既に存在する場合-上書き)
これはハッキングであり、本番コードでは使用しないでください。
var b = srcDictionary.Select(
x=>
{
destDictionary[x.Key] = x.Value;
return true;
}
).Count();
Jon Skeetに触発され、私は彼のソリューションを次のように拡張しました。
拡張方法:
public static void Execute<TSource, TKey>(this IEnumerable<TSource> source, Action<TKey> applyBehavior, Func<TSource, TKey> keySelector)
{
foreach (var item in source)
{
var target = keySelector(item);
applyBehavior(target);
}
}
クライアント:
var jobs = new List<Job>()
{
new Job { Id = "XAML Developer" },
new Job { Id = "Assassin" },
new Job { Id = "Narco Trafficker" }
};
jobs.Execute(ApplyFilter, j => j.Id);
。 。 。
public void ApplyFilter(string filterId)
{
Debug.WriteLine(filterId);
}
ForEachを Chained にすることもできます。アクションの後には、パイルラインに戻すだけです。 流なまま
Employees.ForEach(e=>e.Act_A)
.ForEach(e=>e.Act_B)
.ForEach(e=>e.Act_C);
Orders //just for demo
.ForEach(o=> o.EmailBuyer() )
.ForEach(o=> o.ProcessBilling() )
.ForEach(o=> o.ProcessShipping());
//conditional
Employees
.ForEach(e=> { if(e.Salary<1000) e.Raise(0.10);})
.ForEach(e=> { if(e.Age >70 ) e.Retire();});
実装の熱心なバージョン。
public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enu, Action<T> action)
{
foreach (T item in enu) action(item);
return enu; // make action Chainable/Fluent
}
編集: Lazy バージョンは、 this 。
public static IEnumerable<T> ForEachLazy<T>(this IEnumerable<T> enu, Action<T> action)
{
foreach (var item in enu)
{
action(item);
yield return item;
}
}
具体化するレイジーバージョン必要、たとえばToList()。それ以外の場合は何も起こりません。 ToolmakerSteveからのすばらしいコメントをご覧ください。
IQueryable<Product> query = Products.Where(...);
query.ForEachLazy(t => t.Price = t.Price + 1.00)
.ToList(); //without this line, below SubmitChanges() does nothing.
SubmitChanges();
ForEach()とForEachLazy()の両方をライブラリに保持しています。
リンク拡張メソッドには副作用がないという考え方に敬意を表しません(そうではないだけでなく、どのデリゲートでも副作用を実行できます)。
次のことを考慮してください:
public class Element {}
public Enum ProcessType
{
This = 0, That = 1, SomethingElse = 2
}
public class Class1
{
private Dictionary<ProcessType, Action<Element>> actions =
new Dictionary<ProcessType,Action<Element>>();
public Class1()
{
actions.Add( ProcessType.This, DoThis );
actions.Add( ProcessType.That, DoThat );
actions.Add( ProcessType.SomethingElse, DoSomethingElse );
}
// Element actions:
// This example defines 3 distict actions
// that can be applied to individual elements,
// But for the sake of the argument, make
// no assumption about how many distict
// actions there may, and that there could
// possibly be many more.
public void DoThis( Element element )
{
// Do something to element
}
public void DoThat( Element element )
{
// Do something to element
}
public void DoSomethingElse( Element element )
{
// Do something to element
}
public void Apply( ProcessType processType, IEnumerable<Element> elements )
{
Action<Element> action = null;
if( ! actions.TryGetValue( processType, out action ) )
throw new ArgumentException("processType");
foreach( element in elements )
action(element);
}
}
実際に示しているのは、要素のシーケンスに副作用を持つ多くの可能性のあるアクションの1つを呼び出すことができる一種の遅延バインディングです。アクションを実行し、対応するメソッドに変換します。
VB.NETの場合:
listVariable.ForEach(Sub(i) i.Property = "Value")
流stayに保つには、次のようなトリックを使用できます。
GetItems()
.Select(i => new Action(i.DoStuf)))
.Aggregate((a, b) => a + b)
.Invoke();
MoreLinqには、 IEnumerable&lt; T&gt; .ForEach
およびその他の便利な拡張機能が多数あります。おそらく ForEach
のためだけに依存関係を取る価値はありませんが、そこには多くの有用なものがあります。
さらに別の ForEach
の例
public static IList<AddressEntry> MapToDomain(IList<AddressModel> addresses)
{
var workingAddresses = new List<AddressEntry>();
addresses.Select(a => a).ToList().ForEach(a => workingAddresses.Add(AddressModelMapper.MapToDomain(a)));
return workingAddresses;
}